"""
Chainlit应用主文件

债券分析助手的主入口，使用async with Step上下文管理器确保步骤顺序正确显示。
"""

import chainlit as cl
import logging
import uuid
import json
from typing import Optional, Dict, Any
from datetime import datetime
import plotly.graph_objects as go

# 导入工作流管理器和记忆管理器
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))

from workflows.workflow_manager import workflow_manager
from memory import memory_manager
from logging_config import setup_logging, setup_stdlib_logging, get_logger


# 配置日志 - 使用统一的日志系统，过滤调试信息
setup_logging(log_level="WARNING")  # 只显示WARNING及以上级别的日志
setup_stdlib_logging()  # 配置第三方库日志
logger = get_logger("ui.app")


def _safe_json_parse(data):
    """安全解析JSON数据"""
    try:
        if isinstance(data, str):
            return json.loads(data)
        elif isinstance(data, dict):
            return data
        else:
            return None
    except json.JSONDecodeError:
        return None


def _safe_get(d, *keys, default=None):
    """
    安全的多级字典访问
    
    Args:
        d: 字典对象
        *keys: 多级键路径
        default: 默认值
        
    Returns:
        获取到的值或默认值
        
    Examples:
        _safe_get(data, "a", "b", "c", default=0)
        等价于 data.get("a", {}).get("b", {}).get("c", 0)
        但更安全，不会因为中间值为None而报错
    """
    result = d
    for key in keys:
        if result is None or not isinstance(result, dict):
            return default
        result = result.get(key)
    return result if result is not None else default


def _generate_radar_chart(radar_data: dict) -> str:
    """生成Plotly雷达图HTML代码"""
    # 从JSON数据中提取维度名称和评分
    dimensions = radar_data.get("dimensions", [])
    scores = radar_data.get("scores", [])
    max_score = radar_data.get("max_score", 10)
    average_score = radar_data.get("average_score", 0)
    
    # 创建雷达图
    fig = go.Figure()
    
    # 添加雷达图轨迹
    fig.add_trace(go.Scatterpolar(
        r=scores + [scores[0]] if scores else [],  # 闭合图形
        theta=dimensions + [dimensions[0]] if dimensions else [],  # 闭合图形
        fill='toself',
        name='维度评分',
        line=dict(color='rgb(31, 119, 180)'),
        fillcolor='rgba(31, 119, 180, 0.2)'
    ))
    
    # 设置雷达图布局
    fig.update_layout(
        polar=dict(
            radialaxis=dict(
                visible=True,
                range=[0, max_score],
                tickvals=list(range(0, max_score + 1, 2)),
                ticktext=[str(i) for i in range(0, max_score + 1, 2)]
            )
        ),
        showlegend=False,
        title=f"股票分析雷达图 (平均分: {average_score:.1f})",
        title_x=0.5,
        width=600,
        height=500
    )
    
    # 转换为HTML
    return fig.to_html(include_plotlyjs='cdn', div_id="radar-chart")


@cl.on_chat_start
async def start():
    """
    会话开始时的初始化
    """
    try:
        # 生成唯一的线程ID
        thread_id = str(uuid.uuid4())
        cl.user_session.set("thread_id", thread_id)
        
        # 初始化对话历史
        cl.user_session.set("chat_history", [])
        
        # 获取记忆统计信息
        stats = memory_manager.get_statistics()
        summary = stats.get("summary", {})
        
        # 获取常分析标的
        freq_bonds = memory_manager.get_frequently_analyzed_bonds(top_n=5)
        freq_bonds_str = ""
        if freq_bonds:
            freq_bonds_list = [f"{code} ({count}次)" for code, count in freq_bonds]
            freq_bonds_str = f"\n📊 **常分析标的**: {', '.join(freq_bonds_list)}"
        
        # 构建欢迎消息（包含统计信息）
        welcome_message = f"""
# 🎯 债券分析助手

欢迎使用智能债券分析助手！我可以帮您：

## 📈 系统统计
- 已分析债券: **{summary.get('total_bonds_analyzed', 0)}** 个
- 分析次数: **{summary.get('total_analyses', 0)}** 次
- 会话记录: **{summary.get('total_threads', 0)}** 个{freq_bonds_str}

## 功能介绍

### 1️⃣ 债券分析
对单个债券进行全面的信用分析、条款分析、宏观分析和估值分析。

**示例**：
- "分析南航转债"
- "帮我看看113050的投资价值"
- "分析债券127011"

### 2️⃣ 债券对比
横向对比2-5个债券的投资价值。

**示例**：
- "对比113050和113051"
- "帮我对比南航转债、中矿转债和宁行转债"
- "比较这三个债券：127011、127012、127013"

### 3️⃣ 信息检索
快速检索债券信息、筛选符合条件的债券。

**示例**：
- "查找AA+评级的债券"
- "有哪些高收益率的债券？"
- "查询113050的最新价格"

### 4️⃣ 智能问答
回答债券和股票相关的问题。

**示例**：
- "什么是可转债？"
- "如何判断债券的投资价值？"
- "转股价值是什么意思？"

### 5️⃣ 股票分析
对单个股票进行全面的技术、消息、估值、财务、资金分析。

**示例**：
- "分析000001股票"
- "帮我看看平安银行的投资价值"
- "分析贵州茅台"

---

💡 **提示**: 您可以直接输入问题或需求，系统会自动识别您的意图并执行相应的分析！

🧠 **智能记忆**: 系统会记住您的分析历史和债券信息，提供更智能的上下文感知服务。
"""
        
        await cl.Message(content=welcome_message).send()
        
        logger.info(f"新会话开始，线程ID: {thread_id}")
        
    except Exception as e:
        logger.error(f"会话初始化失败: {e}")
        await cl.Message(content=f"初始化失败: {str(e)}").send()


@cl.on_message
async def main(message: cl.Message):
    """
    处理用户消息的主函数
    
✅ 使用LangGraph主图统一入口，所有工作流通过主图路由执行
    ✅ 所有内容（包括最终报告）都作为Message显示，确保按创建顺序排列
    
    Args:
        message: 用户发送的消息
    """
    try:
        # 获取会话信息
        thread_id = cl.user_session.get("thread_id")
        chat_history = cl.user_session.get("chat_history") or []
        
        # 记录用户消息
        user_query = message.content
        logger.info(f"收到用户消息: {user_query}")
        
        # 添加到对话历史
        if not isinstance(chat_history, list):
            chat_history = []
        
        chat_history.append({
            "role": "user",
            "content": user_query,
            "timestamp": datetime.now().isoformat()
        })
        
        # 构建最近对话历史上下文（最多保留最近5轮）
        recent_history = ""
        if len(chat_history) > 1:
            recent_messages = chat_history[-5:]
            recent_history = "\n".join([
                f"{msg['role']}: {msg['content']}"
                for msg in recent_messages[:-1]
            ])
        
        # ========== ✅ 使用主图统一入口执行 ==========
        result_content = ""
        active_steps = {}  # 跟踪活动的Step对象
        investment_charts = {}  # 初始化图表数据
        stock_chart_elements = []  # 初始化股票图表元素
        
        # 流式执行主图工作流
        async for event in workflow_manager.stream_execute(
            user_query=user_query,
            thread_id=thread_id,
            recent_history=recent_history
        ):
            event_type = event.get("type")
            node = event.get("node", "")
            
            # 跳过某些内部节点的显示（如上下文加载、记忆保存、对话处理）
            skip_nodes = ["context_loading", "memory_saving", "conversation"]
            if node in skip_nodes:
                continue
            
            # 处理step_start事件 - 使用async with确保Step在Message之前发送
            if event_type == "step_start":
                step_name = event.get("step_name", node)
                
                # 不显示意图识别节点
                if node == "intent_recognition":
                    continue
                
                # ✅ 使用async with创建并立即发送Step
                async with cl.Step(name=step_name, type="run") as step:
                    step.output = "🔄 正在执行..."+ "\n"
                # ✅ 退出上下文后，Step已发送，但仍可通过引用更新
                active_steps[node] = step
            
            # 处理reasoning事件 - 显示推理过程
            elif event_type == "reasoning":
                reasoning_content = event.get("content", "")
                if node in active_steps and reasoning_content:
                    # 累加推理内容
                    #current_output = active_steps[node].output or ""
                    #if "🔄 正在执行..." in current_output:
                    #    current_output = ""  # 清除初始状态
                    #active_steps[node].output = current_output + reasoning_content + "\n"
                    #await active_steps[node].update()
                    await active_steps[node].stream_token(reasoning_content)
            
            # 处理final_answer事件
            elif event_type == "final_answer":
                if node in active_steps:
                    active_steps[node].output = (active_steps[node].output or "") + "\n✅ 执行完成"
                    await active_steps[node].update()
            
            # 处理step_end事件
            elif event_type == "step_end":
                if node in active_steps:
                    # 确保显示完成状态
                    if "✅" not in active_steps[node].output:
                        active_steps[node].output = (active_steps[node].output or "") + "\n✅ 执行完成"
                        await active_steps[node].update()
                    # 移除Step引用
                    del active_steps[node]
            
            # 处理error事件
            elif event_type == "error":
                error_msg = event.get("error", "未知错误")
                logger.error(f"节点{node}执行错误: {error_msg}")
                if node in active_steps:
                    active_steps[node].output = f"❌ 错误: {error_msg}"
                    await active_steps[node].update()
                    del active_steps[node]
            
            # 处理workflow_complete事件 - 提取最终结果
            elif event_type == "workflow_complete":
                state = event.get("state", {})
                workflow_type = state.get("workflow_type", "")
                
                # 添加调试日志
                logger.info(f"🔍 workflow_complete事件: workflow_type={workflow_type}, state键={list(state.keys())}")
                
                # ✅ 根据工作流类型从subgraph_state提取并格式化结果
                if workflow_type == "conversation":
                    # 对话工作流：从conversation_result获取
                    result_content = state.get("conversation_result", "")
                    investment_charts = {}
                    logger.info(f"✅ 对话工作流结果: result_content长度={len(result_content)}")
                    
                elif workflow_type == "analysis":
                    # ✅ 分析工作流：从subgraph_state获取完整状态并格式化
                    subgraph_state = state.get("subgraph_state", {})
                    if subgraph_state:
                        result_content = _format_analysis_result(subgraph_state)
                        logger.info(f"✅ 债券分析工作流结果: subgraph_state键={list(subgraph_state.keys())}")
                    else:
                        result_content = "❌ 分析结果为空"
                        logger.warning("⚠️ 债券分析工作流: subgraph_state为空")
                    investment_charts = {}
                    
                elif workflow_type == "comparison":
                    # 对比工作流：从subgraph_state获取
                    subgraph_state = state.get("subgraph_state", {})
                    if subgraph_state:
                        result_content = _format_comparison_result(subgraph_state)
                        logger.info(f"✅ 债券对比工作流结果: subgraph_state键={list(subgraph_state.keys())}")
                    else:
                        result_content = "❌ 对比结果为空"
                        logger.warning("⚠️ 债券对比工作流: subgraph_state为空")
                    investment_charts = {}
                    
                elif workflow_type == "stock_analysis":
                    # ✅ 股票分析工作流：从subgraph_state获取完整状态并格式化
                    subgraph_state = state.get("subgraph_state", {})
                    if subgraph_state:
                        logger.info(f"🔍 股票分析工作流: subgraph_state键={list(subgraph_state.keys())}")
                        result_content, investment_charts, stock_chart_elements = _format_stock_analysis_result(subgraph_state)
                        logger.info(f"✅ 股票分析工作流结果: result_content长度={len(result_content)}, investment_charts键={list(investment_charts.keys())}")
                    else:
                        result_content = "❌ 股票分析结果为空"
                        investment_charts = {}
                        logger.warning("⚠️ 股票分析工作流: subgraph_state为空")

                elif workflow_type == "query":
                    # 检索工作流：从subgraph_state获取
                    subgraph_state = state.get("subgraph_state", {})
                    if subgraph_state:
                        result_content = _format_query_result(subgraph_state)
                        logger.info(f"✅ 信息检索工作流结果: subgraph_state键={list(subgraph_state.keys())}")
                    else:
                        result_content = "❌ 检索结果为空"
                        logger.warning("⚠️ 信息检索工作流: subgraph_state为空")
                    investment_charts = {}

                else:
                    # 未知类型，尝试从多个字段提取
                    result_content = state.get("conversation_result", "") or "❌ 未知工作流类型"
                    investment_charts = {}
                    logger.warning(f"⚠️ 未知工作流类型: {workflow_type}")
        
        # ========== ✅ 将最终结果作为Message流式显示（包含嵌入的图表元素） ==========
        if result_content:
            # 检查是否包含HTML内容（雷达图）
            if "<div" in result_content or "<html" in result_content:
                # 对于包含HTML的内容，直接发送而不流式显示
                await cl.Message(content=result_content).send()
            else:
                # 创建Message对象
                msg = cl.Message(content="")
                
                # 按行分割内容
                lines = result_content.split('\n')
                
                # 逐行流式输出
                for line in lines:
                    await msg.stream_token(line + '\n')
                
                # ========== ✅ 修复：增强图表数据嵌入逻辑，支持多层级查找 ==========
                elements = []
                
                # 记录调试信息
                logger.info(f"开始嵌入图表数据，当前investment_charts类型: {type(investment_charts)}")
                
                if investment_charts and isinstance(investment_charts, dict) and len(investment_charts) > 0:
                    logger.info(f"检测到图表数据，准备嵌入图表: {list(investment_charts.keys())}")
                    
                    # 渲染五维分析雷达图
                    if "five_dimension_radar" in investment_charts:
                        radar_data = investment_charts["five_dimension_radar"]
                        logger.info(f"五维分析雷达图数据内容: {radar_data}")
                        
                        if isinstance(radar_data, dict) and "dimensions" in radar_data and "scores" in radar_data:
                            categories = radar_data["dimensions"]
                            scores = radar_data["scores"]
                            max_score = radar_data.get("max_score", 10)
                            average_score = radar_data.get("average_score", 5.0)
                            
                            logger.info(f"雷达图维度: {categories}, 评分: {scores}, 最大分: {max_score}, 平均分: {average_score}")
                            
                            # 确保数据有效
                            if categories and scores and len(categories) == len(scores):
                                try:
                                    # 创建Plotly雷达图
                                    import plotly.graph_objects as go
                                    fig = go.Figure()
                                    
                                    fig.add_trace(go.Scatterpolar(
                                        r=scores + [scores[0]],  # 闭合图形
                                        theta=categories + [categories[0]],  # 闭合图形
                                        fill='toself',
                                        name='维度评分',
                                        line=dict(color='rgb(31, 119, 180)'),
                                        fillcolor='rgba(31, 119, 180, 0.2)'
                                    ))
                                    
                                    fig.update_layout(
                                        polar=dict(
                                            radialaxis=dict(
                                                visible=True,
                                                range=[0, max_score],
                                                tickvals=list(range(0, max_score + 1, 2)),
                                                ticktext=[str(i) for i in range(0, max_score + 1, 2)]
                                            )
                                        ),
                                        showlegend=False,
                                        title=f"五维分析雷达图 (平均分: {average_score:.1f})",
                                        title_x=0.5,
                                        width=600,
                                        height=500
                                    )
                                    
                                    # 创建Plotly元素并添加到消息中
                                    elements.append(cl.Plotly(name="五维分析雷达图", figure=fig, display="inline"))
                                    logger.info("五维分析雷达图嵌入成功")
                                except Exception as e:
                                    logger.error(f"五维分析雷达图嵌入失败: {e}")
                            else:
                                logger.error(f"五维分析雷达图数据无效: 维度数{len(categories)}, 评分数{len(scores)}")
                        else:
                            logger.error(f"五维分析雷达图数据缺失关键字段: dimensions={ 'dimensions' in radar_data }, scores={ 'scores' in radar_data }")
                    
                    # 渲染投资建议分布图
                    if "investment_advice" in investment_charts:
                        advice_data = investment_charts["investment_advice"]
                        logger.info(f"投资建议分布图数据内容: {advice_data}")
                        
                        if isinstance(advice_data, dict) and "labels" in advice_data and "values" in advice_data:
                            try:
                                # 创建条形图
                                import plotly.graph_objects as go
                                fig = go.Figure()
                                
                                fig.add_trace(go.Bar(
                                    x=advice_data["labels"],
                                    y=advice_data["values"],
                                    marker_color='rgb(55, 83, 109)'
                                ))
                                
                                fig.update_layout(
                                    title="投资建议分布图",
                                    xaxis_title="投资建议",
                                    yaxis_title="分布值",
                                    height=400
                                )
                                
                                elements.append(cl.Plotly(name="投资建议分布图", figure=fig, display="inline"))
                                logger.info("投资建议分布图嵌入成功")
                            except Exception as e:
                                logger.error(f"投资建议分布图嵌入失败: {e}")
                        else:
                            logger.error("投资建议分布图数据缺失labels或values字段")
                    
                    # 渲染风险收益散点图
                    if "risk_reward" in investment_charts:
                        risk_data = investment_charts["risk_reward"]
                        logger.info(f"风险收益散点图数据内容: {risk_data}")
                        
                        if isinstance(risk_data, dict) and "x" in risk_data and "y" in risk_data:
                            try:
                                # 创建散点图
                                import plotly.graph_objects as go
                                fig = go.Figure()
                                
                                fig.add_trace(go.Scatter(
                                    x=risk_data["x"],
                                    y=risk_data["y"],
                                    mode='markers',
                                    marker=dict(
                                        size=risk_data.get("sizes", [20])[0],
                                        color=risk_data.get("colors", ['#1f77b4'])[0]
                                    ),
                                    name=risk_data.get("names", ["当前股票"])[0]
                                ))
                                
                                fig.update_layout(
                                    title="风险收益散点图",
                                    xaxis_title=risk_data.get("x_label", "风险评分"),
                                    yaxis_title=risk_data.get("y_label", "价值评分"),
                                    height=400
                                )
                                
                                elements.append(cl.Plotly(name="风险收益散点图", figure=fig, display="inline"))
                                logger.info("风险收益散点图嵌入成功")
                            except Exception as e:
                                logger.error(f"风险收益散点图嵌入失败: {e}")
                        else:
                            logger.error("风险收益散点图数据缺失x或y字段")
                else:
                    logger.warning("investment_charts为空或无效，无法嵌入图表")
                
                # 合并所有图表元素
                all_elements = []
                if elements:
                    all_elements.extend(elements)
                    logger.info(f"添加投资总结图表 {len(elements)} 个")
                if stock_chart_elements:
                    all_elements.extend(stock_chart_elements)
                    logger.info(f"添加技术分析图表 {len(stock_chart_elements)} 个")
                
                # 如果有图表元素，将它们添加到消息中
                if all_elements:
                    msg.elements = all_elements
                    logger.info(f"成功嵌入 {len(all_elements)} 个图表元素到消息中")
                else:
                    logger.warning("没有可嵌入的图表元素")
                
                # 完成发送
                await msg.send()
        
        # 记录助手回复到历史
        chat_history.append({
            "role": "assistant",
            "content": result_content,
            "timestamp": datetime.now().isoformat()
        })
        
        # 更新会话历史
        cl.user_session.set("chat_history", chat_history)
        
        logger.info(f"消息处理完成: {user_query[:50]}...")
        
    except Exception as e:
        logger.error(f"处理消息失败: {e}", exc_info=True)
        # ✅ 错误时也作为Step显示
        error_step = cl.Step(name="❌ 错误", type="run")
        error_step.output = f"处理消息时发生错误\n\n**错误信息**: {str(e)}"
        await error_step.send()


def _format_analysis_result(subgraph_state: Dict[str, Any]) -> str:
    """
    格式化债券分析结果（从子图状态）
    
    ✅ 只显示有内容的维度，避免空白章节
    
    Args:
        subgraph_state: 子图完整状态字典
        
    Returns:
        格式化后的分析报告
    """
    bond_code = subgraph_state.get("bond_code", "未知")
    
    # ✅ 从子图状态提取各个分析结果（已改为字符串类型）
    credit_analysis = subgraph_state.get("credit_analysis", "")
    terms_analysis = subgraph_state.get("terms_analysis", "")
    macro_analysis = subgraph_state.get("macro_analysis", "")
    value_analysis = subgraph_state.get("value_analysis", "")
    final_summary = subgraph_state.get("final_summary", "")
    
    # ✅ 构建最终结果，只包含有内容的维度
    sections = [f"# 📊 债券分析报告 - {bond_code}\n"]
    
    if credit_analysis.strip():
        sections.append(f"## 🏦 发行人信用分析\n{credit_analysis}\n")
    
    if terms_analysis.strip():
        sections.append(f"## 📜 债券条款分析\n{terms_analysis}\n")
    
    if macro_analysis.strip():
        sections.append(f"## 🌍 宏观行业分析\n{macro_analysis}\n")
    
    if value_analysis.strip():
        sections.append(f"## 💎 相对价值分析\n{value_analysis}\n")
    
    if final_summary.strip():
        sections.append(f"## 📊 投资总结\n{final_summary}")
    
    return "\n".join(sections)


def _format_comparison_result(subgraph_state: Dict[str, Any]) -> str:
    """
    格式化债券对比结果（从子图状态）
    
    Args:
        subgraph_state: 子图完整状态字典
        
    Returns:
        格式化后的对比报告
    """
    # ✅ 根据对比工作流的状态结构进行格式化（已改为字符串类型）
    comparison_result = subgraph_state.get("comparison_result", "")
    if comparison_result:
        return comparison_result
    
    # 备用格式化
    return f"# 🔄 债券对比分析\n\n{str(subgraph_state)}"


def _format_stock_analysis_result(subgraph_state: Dict[str, Any]) -> tuple[str, Dict[str, Any], list]:
    """
    格式化股票分析结果（从子图状态），支持雷达图显示和投资总结图表

    ✅ 支持JSON格式结果和雷达图可视化
    ✅ 向后兼容原有文本格式
    ✅ 修复：正确处理新的子图状态结构
    ✅ 新增：支持投资总结智能体生成的图表数据
    ✅ 增强：添加防御性检查，避免NoneType错误

    Args:
        subgraph_state: 子图完整状态字典

    Returns:
        tuple: (格式化后的股票分析报告, 图表数据字典, 图表元素列表)
    """
    # ✅ 防御性检查：确保subgraph_state不为None且为字典
    if not subgraph_state or not isinstance(subgraph_state, dict):
        logger.error("❌ subgraph_state为空或非字典类型")
        return "❌ 分析结果数据异常，请稍后重试", {}, []
    
    investment_charts = {}
    
    try:
        # 检查是否为JSON格式结果
        if isinstance(subgraph_state, dict) and "comprehensive_analysis" in subgraph_state:
            # JSON格式处理 - 使用安全的字典访问
            analysis = _safe_get(subgraph_state, "comprehensive_analysis", default={})
            radar_data = _safe_get(subgraph_state, "radar_chart_data", default={})
            
            # 生成雷达图HTML
            radar_html = _generate_radar_chart(radar_data)
            
            # 安全获取维度评分
            dimension_scores = _safe_get(analysis, "dimension_scores", default={})
            key_highlights = _safe_get(analysis, "key_highlights", default=[])
            risk_control = _safe_get(analysis, "risk_control_advice", default={})
            
            # 构建响应消息
            response = f"""
## 📊 股票分析结果 - {_safe_get(subgraph_state, "stock_code", default="未知")}

### 📈 综合评分: **{_safe_get(analysis, "overall_score", default=0):.1f}/10**

**投资建议**: {_safe_get(analysis, "investment_recommendation", default="未知")}
**风险等级**: {_safe_get(analysis, "risk_assessment", default="未知")}
**仓位建议**: {_safe_get(analysis, "position_suggestion", default="未知")}

### 🔍 维度评分详情
- **技术面**: {_safe_get(dimension_scores, "technical", default=0):.1f}
- **消息面**: {_safe_get(dimension_scores, "news", default=0):.1f}
- **估值面**: {_safe_get(dimension_scores, "valuation", default=0):.1f}
- **财务面**: {_safe_get(dimension_scores, "fundamental", default=0):.1f}
- **资金面**: {_safe_get(dimension_scores, "capital_flow", default=0):.1f}

### 📋 关键亮点
{chr(10).join([f"- {highlight}" for highlight in key_highlights]) if key_highlights else "- 暂无关键亮点"}

### 📊 雷达图可视化
{radar_html}

### ⚠️ 风险控制建议
{_safe_get(risk_control, "stop_loss", default="未知")}
{_safe_get(risk_control, "position_control", default="未知")}
{_safe_get(risk_control, "monitoring_focus", default="未知")}
{_safe_get(risk_control, "risk_warning", default="未知")}

*分析时间: {_safe_get(subgraph_state, "timestamp", default="未知")}*
"""
        else:
            # ✅ 修复：正确处理新的子图状态结构 - 使用安全的字典访问
            stock_code = _safe_get(subgraph_state, "stock_code", default="未知")

            # ✅ 从子图状态提取各个分析结果（支持两种状态结构）
            # 尝试从analysis_results中提取（新的结构）
            analysis_results = _safe_get(subgraph_state, "analysis_results", default={})
            
            # 如果analysis_results存在，使用新的结构
            if analysis_results:
                technical_analysis = _safe_get(analysis_results, "technical", default="")
                news_analysis = _safe_get(analysis_results, "news", default="")
                valuation_analysis = _safe_get(analysis_results, "valuation", default="")
                fundamental_analysis = _safe_get(analysis_results, "fundamental", default="")
                capital_flow_analysis = _safe_get(analysis_results, "capital_flow", default="")
                investment_summary = _safe_get(analysis_results, "summary", default="")
            else:
                # 否则使用原有的字段名（向后兼容）
                technical_analysis = _safe_get(subgraph_state, "technical_analysis", default="")
                news_analysis = _safe_get(subgraph_state, "news_analysis", default="")
                valuation_analysis = _safe_get(subgraph_state, "valuation_analysis", default="")
                fundamental_analysis = _safe_get(subgraph_state, "fundamental_analysis", default="")
                capital_flow_analysis = _safe_get(subgraph_state, "capital_flow_analysis", default="")
                investment_summary = _safe_get(subgraph_state, "investment_summary", default="")

        # ✅ 构建最终结果，只包含有内容的维度
        sections = [f"# 📈 股票分析报告 - {stock_code}\n"]
        chart_elements = []  # 收集所有图表元素

        # 技术分析章节 + 图表
        if technical_analysis and isinstance(technical_analysis, str) and technical_analysis.strip():
            sections.append(f"## 📊 技术分析\n{technical_analysis}\n")
            
            # 🆕 提取并渲染技术分析图表
            technical_charts = _safe_get(subgraph_state, "technical_charts")
            if technical_charts and isinstance(technical_charts, dict) and technical_charts.get("success"):
                logger.info(f"检测到技术分析图表数据，开始渲染")
                
                # 1. K线图
                if "candlestick" in technical_charts:
                    try:
                        candlestick_data = technical_charts["candlestick"]
                        fig = go.Figure(data=[
                            go.Candlestick(
                                x=candlestick_data["dates"],
                                open=candlestick_data["open"],
                                high=candlestick_data["high"],
                                low=candlestick_data["low"],
                                close=candlestick_data["close"],
                                name='K线'
                            )
                        ])
                        fig.add_trace(go.Bar(
                            x=candlestick_data["dates"],
                            y=candlestick_data["volume"],
                            name='成交量',
                            marker_color='rgba(100, 100, 100, 0.5)',
                            yaxis='y2'
                        ))
                        fig.update_layout(
                            title=f"{stock_code} K线图",
                            xaxis_title="日期",
                            yaxis_title="价格",
                            height=600,
                            showlegend=True,
                            xaxis_rangeslider_visible=False,
                            yaxis2=dict(title="成交量", overlaying='y', side='right', showgrid=False)
                        )
                        chart_elements.append(cl.Plotly(name=f"{stock_code} K线图", figure=fig, display="inline"))
                        logger.info("✅ K线图渲染成功")
                    except Exception as e:
                        logger.error(f"❌ K线图渲染失败: {e}")
                
                # 2. 技术指标组合图
                if "technical_indicators" in technical_charts:
                    try:
                        from plotly.subplots import make_subplots
                        indicators = technical_charts["technical_indicators"]
                        
                        fig = make_subplots(
                            rows=3, cols=1,
                            shared_xaxes=True,
                            vertical_spacing=0.05,
                            subplot_titles=('价格走势', 'MACD指标', 'RSI指标'),
                            row_heights=[0.5, 0.25, 0.25]
                        )
                        
                        # 价格 + MA
                        fig.add_trace(go.Scatter(x=indicators["dates"], y=indicators["close"], mode='lines', name='收盘价', line=dict(color='#1f77b4')), row=1, col=1)
                        fig.add_trace(go.Scatter(x=indicators["dates"], y=indicators["ma5"], mode='lines', name='MA5', line=dict(color='orange', width=1)), row=1, col=1)
                        fig.add_trace(go.Scatter(x=indicators["dates"], y=indicators["ma20"], mode='lines', name='MA20', line=dict(color='red', width=1)), row=1, col=1)
                        
                        # MACD
                        fig.add_trace(go.Scatter(x=indicators["dates"], y=indicators["macd"], mode='lines', name='MACD', line=dict(color='blue')), row=2, col=1)
                        fig.add_trace(go.Scatter(x=indicators["dates"], y=indicators["macd_signal"], mode='lines', name='Signal', line=dict(color='red')), row=2, col=1)
                        colors = ['green' if x >= 0 else 'red' for x in indicators["macd_hist"]]
                        fig.add_trace(go.Bar(x=indicators["dates"], y=indicators["macd_hist"], name='MACD Hist', marker_color=colors), row=2, col=1)
                        
                        # RSI
                        fig.add_trace(go.Scatter(x=indicators["dates"], y=indicators["rsi"], mode='lines', name='RSI', line=dict(color='purple')), row=3, col=1)
                        fig.add_hline(y=70, line_dash="dash", line_color="red", row="3", col="1")
                        fig.add_hline(y=30, line_dash="dash", line_color="green", row="3", col="1")
                        
                        fig.update_layout(title=f"{stock_code} 技术指标分析", height=800, showlegend=True)
                        chart_elements.append(cl.Plotly(name=f"{stock_code} 技术指标", figure=fig, display="inline"))
                        logger.info("✅ 技术指标图渲染成功")
                    except Exception as e:
                        logger.error(f"❌ 技术指标图渲染失败: {e}")
                
                # 3. 布林带图
                if "bollinger_bands" in technical_charts:
                    try:
                        bollinger = technical_charts["bollinger_bands"]
                        fig = go.Figure()
                        fig.add_trace(go.Scatter(x=bollinger["dates"], y=bollinger["close"], mode='lines', name='收盘价', line=dict(color='#1f77b4')))
                        fig.add_trace(go.Scatter(x=bollinger["dates"], y=bollinger["upper_band"], mode='lines', name='上轨', line=dict(color='red', dash='dash')))
                        fig.add_trace(go.Scatter(x=bollinger["dates"], y=bollinger["middle_band"], mode='lines', name='中轨', line=dict(color='orange')))
                        fig.add_trace(go.Scatter(x=bollinger["dates"], y=bollinger["lower_band"], mode='lines', name='下轨', line=dict(color='green', dash='dash')))
                        fig.update_layout(title=f"{stock_code} 布林带分析", xaxis_title="日期", yaxis_title="价格", height=500, showlegend=True)
                        chart_elements.append(cl.Plotly(name=f"{stock_code} 布林带", figure=fig, display="inline"))
                        logger.info("✅ 布林带图渲染成功")
                    except Exception as e:
                        logger.error(f"❌ 布林带图渲染失败: {e}")
                
                logger.info(f"技术分析共渲染 {len([e for e in chart_elements if '技术' in e.name or 'K线' in e.name or '布林' in e.name])} 个图表")

        if news_analysis and isinstance(news_analysis, str) and news_analysis.strip():
            sections.append(f"## 📰 消息分析\n{news_analysis}\n")

        if valuation_analysis and isinstance(valuation_analysis, str) and valuation_analysis.strip():
            sections.append(f"## 💰 估值分析\n{valuation_analysis}\n")

        if fundamental_analysis and isinstance(fundamental_analysis, str) and fundamental_analysis.strip():
            sections.append(f"## 📈 财务分析\n{fundamental_analysis}\n")
            
            # 🆕 提取并渲染财务分析图表
            fundamental_charts = _safe_get(subgraph_state, "fundamental_charts")
            if fundamental_charts and isinstance(fundamental_charts, dict):
                logger.info(f"检测到财务分析图表数据，开始渲染")
                
                # 1. 财务比率雷达图
                if "financial_ratios_radar" in fundamental_charts:
                    try:
                        radar_chart = fundamental_charts["financial_ratios_radar"]
                        if isinstance(radar_chart, dict) and radar_chart.get("type") == "financial_ratios_radar":
                            ratios = radar_chart.get("data", {})
                            
                            # 准备雷达图数据
                            categories = []
                            values = []
                            
                            ratio_labels = {
                                'roe': 'ROE净资产收益率',
                                'debt_ratio': '资产负债率',
                                'net_margin': '净利率',
                                'asset_turnover': '资产周转率',
                                'current_ratio': '流动比率'
                            }
                            
                            for key, label in ratio_labels.items():
                                if key in ratios:
                                    categories.append(label)
                                    values.append(ratios[key])
                            
                            if categories and values:
                                # 创建Plotly雷达图
                                fig = go.Figure()
                                
                                fig.add_trace(go.Scatterpolar(
                                    r=values + [values[0]],  # 闭合图形
                                    theta=categories + [categories[0]],  # 闭合图形
                                    fill='toself',
                                    name='财务比率',
                                    line=dict(color='rgb(0, 128, 0)'),
                                    fillcolor='rgba(0, 128, 0, 0.2)'
                                ))
                                
                                fig.update_layout(
                                    polar=dict(
                                        radialaxis=dict(
                                            visible=True,
                                            range=[0, 100]
                                        )
                                    ),
                                    showlegend=False,
                                    title=f"{stock_code} 财务比率雷达图",
                                    title_x=0.5,
                                    width=600,
                                    height=500
                                )
                                
                                chart_elements.append(cl.Plotly(name=f"{stock_code} 财务比率雷达图", figure=fig, display="inline"))
                                logger.info("✅ 财务比率雷达图渲染成功")
                    except Exception as e:
                        logger.error(f"❌ 财务比率雷达图渲染失败: {e}")
                
                # 2. 财务趋势图
                if "financial_trend" in fundamental_charts:
                    try:
                        trend_chart = fundamental_charts["financial_trend"]
                        if isinstance(trend_chart, dict) and trend_chart.get("type") == "financial_trend":
                            trend_data = trend_chart.get("data", [])
                            
                            if trend_data:
                                fig = go.Figure()
                                
                                for series in trend_data:
                                    fig.add_trace(go.Scatter(
                                        x=series.get('x', []),
                                        y=series.get('y', []),
                                        mode='lines+markers',
                                        name=series.get('name', ''),
                                        line=dict(color=series.get('color', '#1f77b4'))
                                    ))
                                
                                fig.update_layout(
                                    title=f"{stock_code} 财务趋势图",
                                    xaxis_title=trend_chart.get('x_label', '期间'),
                                    yaxis_title=trend_chart.get('y_label', '金额(万元)'),
                                    height=500,
                                    showlegend=True
                                )
                                
                                chart_elements.append(cl.Plotly(name=f"{stock_code} 财务趋势", figure=fig, display="inline"))
                                logger.info("✅ 财务趋势图渲染成功")
                    except Exception as e:
                        logger.error(f"❌ 财务趋势图渲染失败: {e}")
                
                # 3. 财务结构分析图
                if "financial_structure" in fundamental_charts:
                    try:
                        structure_chart = fundamental_charts["financial_structure"]
                        if isinstance(structure_chart, dict) and structure_chart.get("type") == "financial_structure":
                            structure_data = structure_chart.get("data", {})
                            labels = structure_data.get('labels', [])
                            values = structure_data.get('values', [])
                            
                            if labels and values:
                                fig = go.Figure(data=[go.Pie(
                                    labels=labels,
                                    values=values,
                                    hole=0.3
                                )])
                                
                                fig.update_layout(
                                    title=f"{stock_code} 财务结构分析",
                                    height=500
                                )
                                
                                chart_elements.append(cl.Plotly(name=f"{stock_code} 财务结构", figure=fig, display="inline"))
                                logger.info("✅ 财务结构图渲染成功")
                    except Exception as e:
                        logger.error(f"❌ 财务结构图渲染失败: {e}")
                
                logger.info(f"财务分析共渲染 {len([e for e in chart_elements if '财务' in e.name])} 个图表")

        if capital_flow_analysis and isinstance(capital_flow_analysis, str) and capital_flow_analysis.strip():
            sections.append(f"## 💹 资金分析\n{capital_flow_analysis}\n")

        if investment_summary and isinstance(investment_summary, str) and investment_summary.strip():
            sections.append(f"## 🎯 投资总结\n{investment_summary}")

            # ✅ 修复：增强图表数据获取逻辑，支持多层级查找和验证 - 使用安全访问
            investment_charts = {}
            
            # 记录调试信息
            logger.info(f"开始查找图表数据，子图状态字段: {list(subgraph_state.keys())}")
            
            # 多层级查找图表数据 - 使用安全的字典访问
            chart_data_sources = [
                # 第一优先级：直接字段
                _safe_get(subgraph_state, "investment_charts"),
                # 第二优先级：嵌套在JSON数据中
                _safe_get(subgraph_state, "investment_summary_json", "radar_chart_data"),
                _safe_get(subgraph_state, "investment_summary_json", "charts"),
                # 第三优先级：备用字段
                _safe_get(subgraph_state, "charts"),
                _safe_get(subgraph_state, "visualization_data")
            ]
            
            for chart_source in chart_data_sources:
                if chart_source and isinstance(chart_source, dict) and chart_source:
                    investment_charts = chart_source
                    logger.info(f"✅ 成功获取图表数据: {list(chart_source.keys())}")
                    break
        
            # 验证图表数据完整性 - 使用安全访问
            if investment_charts and isinstance(investment_charts, dict):
                # 确保包含五维雷达图数据
                if "five_dimension_radar" not in investment_charts:
                    # 尝试从其他字段查找雷达图数据
                    radar_fields = ["radar_chart_data", "radar_data", "five_dimension"]
                    for field in radar_fields:
                        if field in investment_charts:
                            radar_data = investment_charts[field]
                            if isinstance(radar_data, dict):
                                investment_charts["five_dimension_radar"] = radar_data
                                logger.info(f"✅ 从字段 '{field}' 提取雷达图数据")
                                break
                    
                    # 如果仍然没有找到，创建默认雷达图数据
                    if "five_dimension_radar" not in investment_charts:
                        # 从分析结果中提取评分 - 使用安全访问
                        dimension_scores = {}
                        for dim in ["technical", "news", "valuation", "fundamental", "capital"]:
                            # 尝试从分析文本中提取评分
                            analysis_text = ""
                            if dim == "technical":
                                analysis_text = _safe_get(subgraph_state, "technical_analysis", default="")
                            elif dim == "news":
                                analysis_text = _safe_get(subgraph_state, "news_analysis", default="")
                            elif dim == "valuation":
                                analysis_text = _safe_get(subgraph_state, "valuation_analysis", default="")
                            elif dim == "fundamental":
                                analysis_text = _safe_get(subgraph_state, "fundamental_analysis", default="")
                            elif dim == "capital":
                                analysis_text = _safe_get(subgraph_state, "capital_flow_analysis", default="")
                            
                            # 使用简单的评分提取逻辑
                            if analysis_text and "评分" in analysis_text:
                                # 简化评分提取
                                dimension_scores[dim] = 6.0  # 默认中等评分
                            else:
                                dimension_scores[dim] = 5.0
                        
                        # 创建默认雷达图数据
                        investment_charts["five_dimension_radar"] = {
                            "dimensions": ["技术面", "消息面", "估值面", "财务面", "资金面"],
                            "scores": [
                                dimension_scores.get("technical", 5.0),
                                dimension_scores.get("news", 5.0),
                                dimension_scores.get("valuation", 5.0),
                                dimension_scores.get("fundamental", 5.0),
                                dimension_scores.get("capital", 5.0)
                            ],
                            "max_score": 10,
                            "average_score": sum(dimension_scores.values()) / len(dimension_scores) if dimension_scores else 5.0
                        }
                        logger.info("✅ 创建默认雷达图数据")
                
                # 验证雷达图数据完整性
                if "five_dimension_radar" in investment_charts:
                    radar_data = _safe_get(investment_charts, "five_dimension_radar", default={})
                    if isinstance(radar_data, dict):
                        dimensions = _safe_get(radar_data, "dimensions", default=[])
                        scores = _safe_get(radar_data, "scores", default=[])
                        logger.info(f"✅ 五维分析雷达图数据验证: 维度={dimensions}, 评分={scores}")
                        
                        # 确保数据格式正确
                        if not dimensions or not scores or len(dimensions) != len(scores):
                            logger.warning("❌ 雷达图数据格式不正确，使用默认数据")
                            investment_charts["five_dimension_radar"] = {
                                "dimensions": ["技术面", "消息面", "估值面", "财务面", "资金面"],
                                "scores": [5.0, 5.0, 5.0, 5.0, 5.0],
                                "max_score": 10,
                                "average_score": 5.0
                            }
                    else:
                        logger.warning("❌ 雷达图数据不是字典类型，使用默认数据")
                        investment_charts["five_dimension_radar"] = {
                            "dimensions": ["技术面", "消息面", "估值面", "财务面", "资金面"],
                            "scores": [5.0, 5.0, 5.0, 5.0, 5.0],
                            "max_score": 10,
                            "average_score": 5.0
                        }
            else:
                logger.warning("❌ 未找到有效的图表数据，使用空数据")
                investment_charts = {}
        
            # 添加图表说明部分
            charts_section = "## 📊 投资总结图表\n\n"
            
            if investment_charts and isinstance(investment_charts, dict) and len(investment_charts) > 0:
                # 添加图表说明
                charts_section += "投资总结智能体生成了以下可视化图表，帮助您更直观地理解分析结果：\n\n"
                
                # 列出生成的图表类型
                chart_types = {
                    "five_dimension_radar": "五维分析雷达图",
                    "investment_advice": "投资建议分布图",
                    "risk_reward": "风险收益散点图"
                }
                
                available_charts = []
                for chart_key, chart_name in chart_types.items():
                    if chart_key in investment_charts:
                        available_charts.append(f"- {chart_name}")
                
                if available_charts:
                    charts_section += "\n".join(available_charts)
                    charts_section += "\n\n💡 **图表说明**: 这些图表基于技术、消息、估值、财务、资金五大维度的分析结果生成，帮助您从不同角度评估股票的投资价值。"
                else:
                    charts_section += "图表数据已生成，正在加载可视化组件..."
            else:
                charts_section += "📊 **图表数据状态**: 图表数据正在生成中，请稍后查看可视化分析结果。\n\n"
                charts_section += "💡 **提示**: 如果图表未显示，可能是数据生成过程中出现了临时问题，但文字分析结果仍然完整有效。"
            
            sections.append(charts_section)

            response = "\n".join(sections)
            
            # ✅ 如果所有内容都为空，返回错误提示
            if len(sections) <= 1:
                response = "❌ 分析结果为空 - 可能是状态结构不匹配，请检查工作流输出"
    
    except Exception as e:
        # ✅ 捕获异常，避免程序崩溃
        logger.error(f"❌ 格式化股票分析结果时发生错误: {e}", exc_info=True)
        return f"❌ 格式化分析结果时发生错误: {str(e)}\n\n请检查数据结构或稍后重试", {}, []
    
    return response, investment_charts, chart_elements


def _format_query_result(subgraph_state: Dict[str, Any]) -> str:
    """
    格式化信息检索结果（从子图状态）

    Args:
        subgraph_state: 子图完整状态字典

    Returns:
        格式化后的检索结果
    """
    # ✅ 根据检索工作流的状态结构进行格式化（已改为字符串类型）
    retrieval_result = subgraph_state.get("retrieval_result", "")
    if retrieval_result:
        return retrieval_result

    # 备用格式化
    return f"# 🔍 检索结果\n\n{str(subgraph_state)}"


@cl.on_chat_end
async def end():
    """
    会话结束时的清理
    """
    try:
        thread_id = cl.user_session.get("thread_id")
        logger.info(f"会话结束，线程ID: {thread_id}")
        
        # 显示会话统计信息
        if thread_id:
            conv_count = memory_manager.conversation.get_conversation_count(thread_id)
            if conv_count > 0:
                await cl.Message(
                    content=f"📝 本次会话共进行了 **{conv_count // 2}** 轮对话，已保存到记忆系统。"
                ).send()
        
    except Exception as e:
        logger.error(f"会话结束清理失败: {e}")


@cl.on_stop
async def on_stop():
    """
    用户停止执行时的处理
    """
    try:
        logger.info("用户停止了执行")
        await cl.Message(content="⏹️ 已停止执行").send()
        
    except Exception as e:
        logger.error(f"停止处理失败: {e}")


# 应用配置
@cl.set_starters
async def set_starters(user: Optional[cl.User] = None, conversation_id: Optional[str] = None):
    """
    设置启动建议
    
    Args:
        user: 用户对象
        conversation_id: 会话ID
    """
    return [
        cl.Starter(
            label="分析债券",
            message="分析南航转债",
            icon="/public/analyze.svg"
        ),
        cl.Starter(
            label="对比债券",
            message="对比113050和113051",
            icon="/public/compare.svg"
        ),
        cl.Starter(
            label="检索信息",
            message="查找AA+评级的债券",
            icon="/public/search.svg"
        ),
        cl.Starter(
            label="智能问答",
            message="什么是可转债？",
            icon="/public/question.svg"
        ),
        cl.Starter(
            label="股票分析",
            message="分析000001股票",
            icon="/public/stock.svg"
        ),
    ]


# 主程序入口
if __name__ == "__main__":
    # 注意：Chainlit应用通常通过命令行启动
    # chainlit run app.py
    logger.info("Chainlit应用已启动")